/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;

import com.android.mtksettingslib.bluetooth.A2dpProfile;
import com.android.mtksettingslib.bluetooth.HeadsetProfile;
import com.android.mtksettingslib.bluetooth.BluetoothCallback;
import com.android.mtksettingslib.bluetooth.BluetoothUtils;
import com.android.mtksettingslib.bluetooth.CachedBluetoothDevice;
import com.android.mtksettingslib.bluetooth.LocalBluetoothManager;
import com.android.mtksettingslib.bluetooth.LocalBluetoothProfile;
import com.android.mtksettingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.mtksettingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.mtksettingslib.bluetooth.LeAudioProfile;
import com.android.mtksettingslib.bluetooth.MapProfile;
import com.android.mtksettingslib.bluetooth.PanProfile;
import com.android.mtksettingslib.bluetooth.PbapServerProfile;
import com.android.mtksettingslib.bluetooth.VolumeControlProfile;
import com.android.settings.R;
import com.android.settingslib.core.lifecycle.Lifecycle;

import java.util.List;

/**
 * This class adds switches for toggling the individual profiles that a Bluetooth device
 * supports, such as "Phone audio", "Media audio", "Contact sharing", etc.
 */
public class BluetoothDetailsProfilesController extends BluetoothDetailsController
        implements BluetoothCallback,Preference.OnPreferenceClickListener,
        LocalBluetoothProfileManager.ServiceListener {
    private static final String KEY_PROFILES_GROUP = "bluetooth_profiles";
    private static final String KEY_BOTTOM_PREFERENCE = "bottom_preference";
    private static final int ORDINAL = 99;
    private static final String KEY_LE_AUDIO_PREFERENCE = "le_audio_preference";
    private static final String KEY_LE_CG_PREFERENCE = "le_cg_preference";
    private static final String KEY_LE_UMS_PREFERENCE = "le_ums_preference";
    static final ParcelUuid[] UUIDS = {
            BluetoothUuid.PBAP_PCE,
            BluetoothUuid.MAP,
            BluetoothUuid.MNS,
            BluetoothUuid.MAS,
    };
    private static BluetoothAdapter mAdapter;
    private Handler mHandler;
    private static final int LE_AUDIO_OPEN_PRE_DISABLED_TIME_UNDER_BOND_BONDED = 4000;
    private static final int LE_AUDIO_OPEN_PRE_DISABLED_TIME_UNDER_BOND_NONE = 6000;
    private static final int LE_AUDIO_CLOSE_PRE_DISABLED_TIME = 3000;
    // private static final int LE_AUDIO_TRY_PAIR_DELAY_TIME = 5000;

    private static final String TAG = "BluetoothDetailsProfilesController";
    @VisibleForTesting
    static final String HIGH_QUALITY_AUDIO_PREF_TAG = "A2dpProfileHighQualityAudio";

    private LocalBluetoothManager mManager;
    private LocalBluetoothProfileManager mProfileManager;
    private CachedBluetoothDevice mCachedDevice;
    private CachedBluetoothDeviceManager mDeviceManager;

    @VisibleForTesting
    PreferenceCategory mProfilesContainer;

    public BluetoothDetailsProfilesController(Context context, PreferenceFragmentCompat fragment,
            LocalBluetoothManager manager, CachedBluetoothDevice device, Lifecycle lifecycle) {
        super(context, fragment, device, lifecycle);
        mManager = manager;
        mProfileManager = mManager.getProfileManager();
        mCachedDevice = device;
        lifecycle.addObserver(this);
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mDeviceManager = mManager.getCachedDeviceManager();
        mHandler = new Handler();
    }

    @Override
    protected void init(PreferenceScreen screen) {
        mProfilesContainer = (PreferenceCategory)screen.findPreference(getPreferenceKey());
        mProfilesContainer.setLayoutResource(R.layout.preference_bluetooth_profile_category);
        // Call refresh here even though it will get called later in onResume, to avoid the
        // list of switches appearing to "pop" into the page.
        refresh();
    }

    /**
     * Creates a switch preference for the particular profile.
     *
     * @param context The context to use when creating the SwitchPreference
     * @param profile The profile for which the preference controls.
     * @return A preference that allows the user to choose whether this profile
     * will be connected to.
     */
    private SwitchPreference createProfilePreference(Context context,
            LocalBluetoothProfile profile) {
        SwitchPreference pref = new SwitchPreference(context);
        pref.setKey(profile.toString());
        pref.setTitle(profile.getNameResource(mCachedDevice.getDevice()));
        pref.setOnPreferenceClickListener(this);
        pref.setOrder(profile.getOrdinal());
        return pref;
    }

    /**
     * Refreshes the state for an existing SwitchPreference for a profile.
     */
    private void refreshProfilePreference(SwitchPreference profilePref,
            LocalBluetoothProfile profile) {
        BluetoothDevice device = mCachedDevice.getDevice();
        profilePref.setEnabled(!mCachedDevice.isBusy());
        if (profile instanceof MapProfile) {
            profilePref.setChecked(device.getMessageAccessPermission()
                    == BluetoothDevice.ACCESS_ALLOWED);
        } else if (profile instanceof PbapServerProfile) {
            profilePref.setChecked(device.getPhonebookAccessPermission()
                    == BluetoothDevice.ACCESS_ALLOWED);
        } else if (profile instanceof PanProfile) {
            profilePref.setChecked(profile.getConnectionStatus(device) ==
                    BluetoothProfile.STATE_CONNECTED);
        } else {
            profilePref.setChecked(profile.isEnabled(device));
        }

        if (profile instanceof A2dpProfile) {
            A2dpProfile a2dp = (A2dpProfile) profile;
            SwitchPreference highQualityPref = (SwitchPreference) mProfilesContainer.findPreference(
                    HIGH_QUALITY_AUDIO_PREF_TAG);
            if (highQualityPref != null) {
                if (a2dp.isEnabled(device) && a2dp.supportsHighQualityAudio(device)) {
                    highQualityPref.setVisible(true);
                    highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
                    highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
                    highQualityPref.setEnabled(!mCachedDevice.isBusy());
                } else {
                    highQualityPref.setVisible(false);
                }
            }
        }
    }

    /**
     * Helper method to enable a profile for a device.
     */
    private void enableProfile(LocalBluetoothProfile profile) {
        final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
        if (profile instanceof PbapServerProfile) {
            bluetoothDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
            // We don't need to do the additional steps below for this profile.
            return;
        }
        if (profile instanceof MapProfile) {
            bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
        }
        profile.setEnabled(bluetoothDevice, true);
    }

    /**
     * Helper method to disable a profile for a device
     */
    private void disableProfile(LocalBluetoothProfile profile) {
        final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
        profile.setEnabled(bluetoothDevice, false);
        if (profile instanceof MapProfile) {
            bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
        } else if (profile instanceof PbapServerProfile) {
            bluetoothDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
        }
    }

    /**
     * When the pref for a bluetooth profile is clicked on, we want to toggle the enabled/disabled
     * state for that profile.
     */
    @Override
    public boolean onPreferenceClick(Preference preference) {
        if (preference.getKey() == KEY_LE_AUDIO_PREFERENCE) {
            Log.e(TAG,"onPreferenceClick,le audio");
            SwitchPreference leAudioPref = (SwitchPreference) preference;
            if (leAudioPref.isChecked()) {
                onLeAudioPrePositiveClick(leAudioPref,true);
            } else {
                onLeAudioPreNegativeClick(leAudioPref,false);
            }
            return true;
        } else if (preference.getKey() == KEY_LE_CG_PREFERENCE ||
                preference.getKey() == KEY_LE_UMS_PREFERENCE) {
            Log.e(TAG,"onPreferenceClick,le profile");
            SwitchPreference leProfilePref = (SwitchPreference) preference;
            if (leProfilePref.isChecked()) {
                onLeProfilePrePositiveClick();
            } else {
                onLeProfilePreNegativeClick();
            }
            return true;
        }
        LocalBluetoothProfile profile = mProfileManager.getProfileByName(preference.getKey());
        if (profile == null) {
            // It might be the PbapServerProfile, which is not stored by name.
            PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
            if (TextUtils.equals(preference.getKey(), psp.toString())) {
                profile = psp;
            } else {
                return false;
            }
        }
        SwitchPreference profilePref = (SwitchPreference) preference;
        if (profilePref.isChecked()) {
            enableProfile(profile);
        } else {
            disableProfile(profile);
        }
        refreshProfilePreference(profilePref, profile);
        return true;
    }


    /**
     * Helper to get the list of connectable and special profiles.
     */
    private List<LocalBluetoothProfile> getProfiles() {
        List<LocalBluetoothProfile> result = mCachedDevice.getConnectableProfiles();
        final BluetoothDevice device = mCachedDevice.getDevice();

        final int pbapPermission = device.getPhonebookAccessPermission();
        // Only provide PBAP cabability if the client device has requested PBAP.

        BluetoothClass bluetoothClass = device.getBluetoothClass();
        ParcelUuid[] uuids = device.getUuids();
        boolean isCarkitFromClass = false;
        boolean isCarkitFromUuid = false;
        if (bluetoothClass != null) {
            isCarkitFromClass =  bluetoothClass.getDeviceClass() ==
                    BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE;
        }
        isCarkitFromUuid = BluetoothUuid.containsAnyUuid(uuids,UUIDS);
        Log.d(TAG,"getProfiles,isCarkitFromClass = " + isCarkitFromClass + ",isCarkitFromUuid = " +
                isCarkitFromUuid + ",device  type: " + device.getType());
        if ((pbapPermission == BluetoothDevice.ACCESS_ALLOWED && (device.getType() != BluetoothDevice.
                DEVICE_TYPE_LE))|| (pbapPermission == BluetoothDevice.ACCESS_REJECTED && (isCarkitFromClass
                || isCarkitFromUuid) && (device.getType() != BluetoothDevice.DEVICE_TYPE_LE))) {
            final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
            result.add(psp);
        }

        final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
        final int mapPermission = device.getMessageAccessPermission();
        if (mapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
            result.add(mapProfile);
        }

        return result;
    }

    /**
     * This is a helper method to be called after adding a Preference for a profile. If that
     * profile happened to be A2dp and the device supports high quality audio, it will add a
     * separate preference for controlling whether to actually use high quality audio.
     *
     * @param profile the profile just added
     */
    private void maybeAddHighQualityAudioPref(LocalBluetoothProfile profile) {
        if (!(profile instanceof A2dpProfile)) {
            return;
        }
        BluetoothDevice device = mCachedDevice.getDevice();
        A2dpProfile a2dp = (A2dpProfile) profile;
        if (a2dp.isProfileReady() && a2dp.supportsHighQualityAudio(device)) {
            SwitchPreference highQualityAudioPref = new SwitchPreference(
                    mProfilesContainer.getContext());
            highQualityAudioPref.setKey(HIGH_QUALITY_AUDIO_PREF_TAG);
            highQualityAudioPref.setVisible(false);
            highQualityAudioPref.setOnPreferenceClickListener(clickedPref -> {
                boolean enable = ((SwitchPreference) clickedPref).isChecked();
                a2dp.setHighQualityAudioEnabled(mCachedDevice.getDevice(), enable);
                ((SwitchPreference) clickedPref).setTitle(a2dp.getHighQualityAudioOptionLabel(device));
                return true;
            });
            mProfilesContainer.addPreference(highQualityAudioPref);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG,"onPause");
        mProfileManager.removeServiceListener(this);
        mManager.getEventManager().unregisterCallback(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG,"onResume");
        mProfileManager.addServiceListener(this);
        mManager.getEventManager().registerCallback(this);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG,"onDestroy");
        if (mHandler != null && mHandler.hasCallbacks(mSetPreEnableRunnable)) {
            mHandler.removeCallbacks(mSetPreEnableRunnable);
            // mHandler.removeCallbacks(mTryPairRunnable);
        }
    }

    @Override
    public void onServiceConnected() {
        refresh();
    }

    @Override
    public void onServiceDisconnected() {
        refresh();
    }

    /**
     * Refreshes the state of the switches for all profiles, possibly adding or removing switches as
     * needed.
     */
    @Override
    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
        Log.d(TAG,"onDeviceBondStateChanged,bondState = " + bondState);
        if (bondState == BluetoothDevice.BOND_BONDING) {
            refresh();
        }
    }

    @Override
    public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
                                                int bluetoothProfile) {
        Log.d(TAG, "onProfileConnectionStateChanged() device: " +
                BluetoothUtils.getMaskAddress(cachedDevice.getDevice()) +
                        ", state: " + state + ", bluetoothProfile: " + bluetoothProfile);
        if (bluetoothProfile == BluetoothProfile.LE_AUDIO) {
            if (state == BluetoothProfile.STATE_CONNECTED) {
                setLeAudioPreEnable();
                if (mHandler.hasCallbacks(mSetPreEnableRunnable)) {
                    Log.d(TAG, "onProfileConnectionStateChanged() removeCallbacks");
                    mHandler.removeCallbacks(mSetPreEnableRunnable);
                }
            }
            refresh();
        }
        if (bluetoothProfile == BluetoothProfile.HEADSET ||
                bluetoothProfile == BluetoothProfile.A2DP) {
            if (state == BluetoothProfile.STATE_CONNECTED) {
                setLeAudioPreEnable();
                if (mHandler.hasCallbacks(mSetPreEnableRunnable)) {
                    Log.d(TAG, "onProfileConnectionStateChanged() removeCallbacks");
                    mHandler.removeCallbacks(mSetPreEnableRunnable);
                }
            }
        }
    }
    @Override
    protected void refresh() {
        if (mCachedDevice.getLeAudioStatus() == BluetoothDevice.LE_ADUIO_STATE_ON) {
            createLeProfilePreference();
            // Hide BR preference
            for (LocalBluetoothProfile profile : getProfiles()) {
                SwitchPreference pref = mProfilesContainer.findPreference(
                        profile.toString());
                if (pref != null) {
                    pref.setVisible(false);
                    // mProfilesContainer.removePreference(pref);
                }
            }
            SwitchPreference highQualityPref = (SwitchPreference) mProfilesContainer.findPreference(
                    HIGH_QUALITY_AUDIO_PREF_TAG);
            if (highQualityPref != null) {
                highQualityPref.setVisible(false);
                // mProfilesContainer.removePreference(highQualityPref);
            }
        } else {
        for (LocalBluetoothProfile profile : getProfiles()) {
            if ((profile == null) || (!profile.isProfileReady())) {
                continue;
            }
            SwitchPreference pref = mProfilesContainer.findPreference(
                    profile.toString());
            if (pref == null) {
                pref = createProfilePreference(mProfilesContainer.getContext(), profile);
                mProfilesContainer.addPreference(pref);
                maybeAddHighQualityAudioPref(profile);
                } else {
                    pref.setVisible(true);
                }
            refreshProfilePreference(pref, profile);
        }
        for (LocalBluetoothProfile removedProfile : mCachedDevice.getRemovedProfiles()) {
            final SwitchPreference pref = mProfilesContainer.findPreference(
                    removedProfile.toString());
            if (pref != null) {
                mProfilesContainer.removePreference(pref);
            }
        }

            // Hide cg and ums preference
            SwitchPreference cgPre = mProfilesContainer.findPreference(KEY_LE_CG_PREFERENCE);
            if (cgPre != null) {
                cgPre.setVisible(false);
                // mProfilesContainer.removePreference(cgPre);
            }

            SwitchPreference umsPre = mProfilesContainer.findPreference(KEY_LE_UMS_PREFERENCE);
            if (umsPre != null) {
                umsPre.setVisible(false);
                // mProfilesContainer.removePreference(umsPre);
            }
        }

        // create le audio preference if need
        if ((mCachedDevice.getAddress()).equals(mCachedDevice.findBrAddress())) {
            createLeAudioPreference();
        }
        Preference preference = mProfilesContainer.findPreference(KEY_BOTTOM_PREFERENCE);
        if (preference == null) {
            preference = new Preference(mContext);
            preference.setLayoutResource(R.layout.preference_bluetooth_profile_category);
            preference.setEnabled(false);
            preference.setKey(KEY_BOTTOM_PREFERENCE);
            preference.setOrder(ORDINAL);
            preference.setSelectable(false);
            mProfilesContainer.addPreference(preference);
        }
    }

    @Override
    public String getPreferenceKey() {
        return KEY_PROFILES_GROUP;
    }

    private void createLeAudioPreference() {
        Log.e(TAG,"createLeAudioPreference");
        SwitchPreference preference = mProfilesContainer.findPreference(KEY_LE_AUDIO_PREFERENCE);
        if (preference == null) {
            preference = new SwitchPreference(mProfilesContainer.getContext());
            preference.setKey(KEY_LE_AUDIO_PREFERENCE);
            preference.setTitle(R.string.bluetooth_le_audio_pre);
            preference.setChecked(mCachedDevice.getLeAudioStatus() ==
                    BluetoothDevice.LE_ADUIO_STATE_ON);
            preference.setOnPreferenceClickListener(this);
            preference.setOrder(BluetoothProfile.MAX_PROFILE_ID);
            mProfilesContainer.addPreference(preference);
        }
    }

    private void createLeProfilePreference() {
        Log.e(TAG,"createLeProfilePreference");
        boolean isbusy = isLeDeviceBusy();
        boolean isConnected = isLeDeviceConnected();
        if (SystemProperties.get("persist.vendor.bluetooth.leaudio_mode", "").
                equalsIgnoreCase("ums-cg")) {
            createCgCallsPreference(isbusy,isConnected);
        }
        createUmsAuidoPreference(isbusy,isConnected);
    }

    private void createCgCallsPreference(boolean isbusy,boolean isConnected) {
        Log.e(TAG,"createCgCallsPreference");
        SwitchPreference cgPre = mProfilesContainer.findPreference(KEY_LE_CG_PREFERENCE);
        if (cgPre == null) {
            cgPre = new SwitchPreference(mProfilesContainer.getContext());
            cgPre.setKey(KEY_LE_CG_PREFERENCE);
            cgPre.setTitle(R.string.bluetooth_le_cg_pre);
            cgPre.setEnabled(!isbusy);
            cgPre.setChecked(isConnected);
            cgPre.setOnPreferenceClickListener(this);
            cgPre.setOrder(BluetoothProfile.MAX_PROFILE_ID - 2);
            mProfilesContainer.addPreference(cgPre);
        } else {
            cgPre.setVisible(true);
            cgPre.setEnabled(!isbusy);
            cgPre.setChecked(isConnected);
        }
    }

    private void createUmsAuidoPreference(boolean isbusy,boolean isConnected) {
        Log.e(TAG,"createUmsAuidoPreference");
        SwitchPreference umsPre = mProfilesContainer.findPreference(KEY_LE_UMS_PREFERENCE);
        if (umsPre == null) {
            umsPre = new SwitchPreference(mProfilesContainer.getContext());
            umsPre.setKey(KEY_LE_UMS_PREFERENCE);
            umsPre.setTitle(R.string.bluetooth_le_ums_pre);
            umsPre.setEnabled(!isbusy);
            umsPre.setChecked(isConnected);
            umsPre.setOnPreferenceClickListener(this);
            umsPre.setOrder(BluetoothProfile.MAX_PROFILE_ID - 1);
            mProfilesContainer.addPreference(umsPre);
        } else {
            umsPre.setVisible(true);
            umsPre.setEnabled(!isbusy);
            umsPre.setChecked(isConnected);
        }
    }

    private void onLeAudioPrePositiveClick(SwitchPreference leAudioPre,boolean status) {
        String leStr = mCachedDevice.findLeAddress();
        String le1Str = "";
        String le2Str = "";
        if (leStr != null) {
            String[] deviceInfo = leStr.split(";");
            if (deviceInfo.length > 1) {
                le1Str = deviceInfo[0];
                le2Str = deviceInfo[1];
            }
        }
        Log.d(TAG,"onLeAudioPrePositiveClick,le1Str = " + le1Str + ",le2Str = " + le2Str);
        if (mAdapter != null && !("".equals(le1Str)) && !("".equals(le2Str))) {
            BluetoothDevice le1Device = mAdapter.getRemoteDevice(le1Str);
            BluetoothDevice le2Device = mAdapter.getRemoteDevice(le2Str);
            int interval = LE_AUDIO_OPEN_PRE_DISABLED_TIME_UNDER_BOND_BONDED;
            //Fix,should figure out which device should pair first.
            boolean isLe1Connected = false;
            boolean isLe2Connected = false;
            int le1BondState = le1Device.getBondState();
            int le2BondState = le2Device.getBondState();
            boolean le1ConnectableState = le1Device.isConnectableDevice();
            Log.d(TAG,"le1BondState = " + le1BondState + ",le2BondState = " + le2BondState +
                    ",le1ConnectableState = " + le1ConnectableState);
            boolean isLe1BondCalled = false;
            if (le1BondState == BluetoothDevice.BOND_NONE && le1ConnectableState) {
                Log.d(TAG, "create bond to LE1");
                le1Device.createBond(BluetoothDevice.TRANSPORT_LE);
                isLe1BondCalled = true;
                interval = LE_AUDIO_OPEN_PRE_DISABLED_TIME_UNDER_BOND_NONE;
            } else if (le1BondState == BluetoothDevice.BOND_BONDED) {
                CachedBluetoothDevice cachedLe1Device = mDeviceManager.findDevice(le1Device);
                if (cachedLe1Device != null) {
                    isLe1Connected = cachedLe1Device.isConnected();
                }
                Log.d(TAG, "connect to LE1");
                LeAudioProfile le_audioProfile = mProfileManager.getLeAudioProfile();
                if (le_audioProfile != null) {
                    le_audioProfile.setEnabled(le1Device,true);
                }
                VolumeControlProfile vc_controlProfile = mProfileManager.getVolumeControlProfile();
                if (vc_controlProfile != null) {
                    vc_controlProfile.setEnabled(le1Device,true);
                }
            }

            if (le2BondState == BluetoothDevice.BOND_NONE && le1BondState !=
                    BluetoothDevice.BOND_BONDING && !isLe1BondCalled) {
                Log.d(TAG, "create bond to LE2");
                le2Device.createBond(BluetoothDevice.TRANSPORT_LE);
                interval = LE_AUDIO_OPEN_PRE_DISABLED_TIME_UNDER_BOND_NONE;
            } else if (le2BondState == BluetoothDevice.BOND_BONDED) {
                CachedBluetoothDevice cachedLe2Device = mDeviceManager.findDevice(le2Device);
                if (cachedLe2Device != null) {
                    isLe2Connected = cachedLe2Device.isConnected();
                }
                Log.d(TAG, "connect to LE2");
                LeAudioProfile le_audioProfile = mProfileManager.getLeAudioProfile();
                if (le_audioProfile != null) {
                    le_audioProfile.setEnabled(le2Device,true);
                }
                VolumeControlProfile vc_controlProfile = mProfileManager.getVolumeControlProfile();
                if (vc_controlProfile != null) {
                    vc_controlProfile.setEnabled(le2Device,true);
                }
            }
            Log.d(TAG, "isLe1Connected = " + isLe1Connected + ",isLe2Connected = " + isLe2Connected);
            if (isLe1Connected || isLe2Connected) {
                BluetoothDevice device = mCachedDevice.getDevice();
                for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
                    if (profile instanceof HeadsetProfile) {
                        profile.setEnabled(device,false);
                    } else if (profile instanceof A2dpProfile) {
                        profile.setEnabled(device,false);
                    }
                }
            }
            Log.d(TAG, "isLe1Connected = " + isLe1Connected + ",isLe2Connected = " + isLe2Connected);
            if (isLe1Connected || isLe2Connected) {
                BluetoothDevice device = mCachedDevice.getDevice();
                for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
                    if (profile instanceof HeadsetProfile) {
                        profile.setEnabled(device,false);
                    } else if (profile instanceof A2dpProfile) {
                        profile.setEnabled(device,false);
                    }
                }
            }
            mCachedDevice.setLeAudioStatus(BluetoothDevice.LE_ADUIO_STATE_ON);
            handleLeAudioPreEnabled(leAudioPre,status,interval);
        }
    }

    private void onLeAudioPreNegativeClick(SwitchPreference leAudioPre,boolean status) {
        // Connect BR first then disconnect Le audio profile
        boolean isHfpConnected = false;
        boolean isA2dpConnected = false;

        for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
            BluetoothDevice device = mCachedDevice.getDevice();
            if (profile instanceof HeadsetProfile) {
                Log.d(TAG, "Set hfp enable");
                if (profile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED) {
                    isHfpConnected = true;
                }
                profile.setEnabled(device,true);
            } else if (profile instanceof A2dpProfile) {
                Log.d(TAG, "Set a2dp enable");
                if (profile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED) {
                    isA2dpConnected = true;
                }
                profile.setEnabled(device, true);
            }
        }
        Log.d(TAG,"onLeAudioPreNegativeClick ,isHfpConnected = " + isHfpConnected +
                ",isA2dpConnected = " + isA2dpConnected);
        // add error handle for quick switch le audio preference
        if (isHfpConnected || isA2dpConnected) {
            String leStr = mCachedDevice.findLeAddress();
            String le1Str = "";
            String le2Str = "";
            if (leStr != null) {
                String[] deviceInfo = leStr.split(";");
                if (deviceInfo.length > 1) {
                    le1Str = deviceInfo[0];
                    le2Str = deviceInfo[1];
                }
            }
            Log.d(TAG,"onLeAudioPreNegativeClick,le1Str = " + le1Str + ",le2Str = " + le2Str);
            if (mAdapter != null && !("".equals(le1Str)) && !("".equals(le2Str))) {
                BluetoothDevice le1Device = mAdapter.getRemoteDevice(le1Str);
                BluetoothDevice le2Device = mAdapter.getRemoteDevice(le2Str);
                LeAudioProfile le_audioProfile = mProfileManager.getLeAudioProfile();
                VolumeControlProfile vc_controlProfile = mProfileManager.getVolumeControlProfile();
                if (le1Device != null) {
                    int le1BondState = le1Device.getBondState();
                    if (le1BondState == BluetoothDevice.BOND_BONDED) {
                        if (le_audioProfile != null) {
                            if (le_audioProfile.getConnectionStatus(le1Device) !=
                                    BluetoothProfile.STATE_DISCONNECTED) {
                                Log.d(TAG, "disconnect to LE1");
                                le_audioProfile.setEnabled(le1Device,false);
                                if (vc_controlProfile != null) {
                                    vc_controlProfile.setEnabled(le1Device,false);
                                }
                            }
                        }
                    } else if (le1BondState == BluetoothDevice.BOND_BONDING) {
                        Log.d(TAG, "Cancel bond to LE1");
                        le1Device.cancelBondProcess();
                    }
                    //TODO:Add error handle
                }

                if (le2Device != null) {
                    int le2BondState = le2Device.getBondState();
                    if (le2BondState == BluetoothDevice.BOND_BONDED) {
                        if (le_audioProfile != null) {
                            if (le_audioProfile.getConnectionStatus(le2Device) !=
                                    BluetoothProfile.STATE_DISCONNECTED) {
                                Log.d(TAG, "disconnect to LE2");
                                le_audioProfile.setEnabled(le1Device,false);
                                if (vc_controlProfile != null) {
                                    vc_controlProfile.setEnabled(le1Device,false);
                                }
                            }
                        }
                    } else if (le2BondState == BluetoothDevice.BOND_BONDING) {
                        Log.d(TAG, "Cancel bond to LE2");
                        le2Device.cancelBondProcess();
                    }
                    //TODO:Add error handle
                }
            }
        }

        mCachedDevice.setLeAudioStatus(BluetoothDevice.LE_ADUIO_STATE_OFF);
        handleLeAudioPreEnabled(leAudioPre,status,LE_AUDIO_CLOSE_PRE_DISABLED_TIME);
    }


    private void onLeProfilePrePositiveClick() {
        String leStr = mCachedDevice.findLeAddress();
        String le1Str = "";
        String le2Str = "";
        if (leStr != null) {
            String[] deviceInfo = leStr.split(";");
            if (deviceInfo.length > 1) {
                le1Str = deviceInfo[0];
                le2Str = deviceInfo[1];
            }
        }
        Log.d(TAG,"onLeProfilePrePositiveClick,le1Str = " + le1Str + ",le2Str = " + le2Str);
        if (mAdapter != null && !("".equals(le1Str)) && !("".equals(le2Str))) {
            BluetoothDevice le1Device = mAdapter.getRemoteDevice(le1Str);
            BluetoothDevice le2Device = mAdapter.getRemoteDevice(le2Str);
            if (le1Device != null) {
                if (le1Device.getBondState() == BluetoothDevice.BOND_BONDED) {
                    Log.d(TAG, "Connect to LE1");
                    mAdapter.connectAllEnabledProfiles(le1Device);
                }
            }

            if (le2Device != null) {
                if (le2Device.getBondState() == BluetoothDevice.BOND_BONDED) {
                    Log.d(TAG, "Connect to LE2");
                    mAdapter.connectAllEnabledProfiles(le2Device);
                }
            }
        }
    }

    private void onLeProfilePreNegativeClick() {
        String leStr = mCachedDevice.findLeAddress();
        String le1Str = "";
        String le2Str = "";
        if (leStr != null) {
            String[] deviceInfo = leStr.split(";");
            if (deviceInfo.length > 1) {
                le1Str = deviceInfo[0];
                le2Str = deviceInfo[1];
            }
        }
        Log.d(TAG,"onLeProfilePreNegativeClick,le1Str = " + le1Str + ",le2Str = " + le2Str);
        if (mAdapter != null && !("".equals(le1Str)) && !("".equals(le2Str))) {
            BluetoothDevice le1Device = mAdapter.getRemoteDevice(le1Str);
            BluetoothDevice le2Device = mAdapter.getRemoteDevice(le2Str);
            if (le1Device != null) {
                if (le1Device.getBondState() == BluetoothDevice.BOND_BONDED) {
                    Log.d(TAG, "disconnect to LE1");
                    mAdapter.disconnectAllEnabledProfiles(le1Device);
                }
            }

            if (le2Device != null) {
                if (le2Device.getBondState() == BluetoothDevice.BOND_BONDED) {
                    Log.d(TAG, "disconnect to LE2");
                    mAdapter.disconnectAllEnabledProfiles(le2Device);
                }
            }
        }
    }

    private void handleLeAudioPreEnabled(SwitchPreference leAudioPre,boolean status,int interval) {
        if (leAudioPre != null) {
            Log.d(TAG,"leAudioPre set disable,status = " + status);
            leAudioPre.setEnabled(false);
            if (status) {
                mHandler.postDelayed(mSetPreEnableRunnable, interval);
                // mHandler.postDelayed(mTryPairRunnable, LE_AUDIO_TRY_PAIR_DELAY_TIME);
            } else {
                mHandler.postDelayed(mSetPreEnableRunnable, interval);
            }
        }
        refresh();
    }

    private Runnable mSetPreEnableRunnable = new Runnable() {
        @Override
        public void run() {
            Log.d(TAG,"mSetPreEnableRunnable run");
            setLeAudioPreEnable();
        }
    };
    private void setLeAudioPreEnable() {
        SwitchPreference leAudioPre = mProfilesContainer.
            findPreference(KEY_LE_AUDIO_PREFERENCE);
        if (leAudioPre != null) {
            if (!(leAudioPre.isEnabled())) {
                Log.d(TAG,"leAudioPre set enable");
                leAudioPre.setEnabled(true);
                refresh();
            }
        }
    }

    private Runnable mTryPairRunnable = new Runnable() {
        @Override
        public void run() {
            String leStr = mCachedDevice.findLeAddress();
            String le1Str = "";
            String le2Str = "";
            if (leStr != null) {
                String[] deviceInfo = leStr.split(";");
                if (deviceInfo.length > 1) {
                    le1Str = deviceInfo[0];
                    le2Str = deviceInfo[1];
                }
            }
            Log.d(TAG,"Retry pair other device ,le1Str = " + le1Str + ",le2Str = " + le2Str);
            if (mAdapter != null && !("".equals(le1Str)) && !("".equals(le2Str))) {
                BluetoothDevice le1Device = mAdapter.getRemoteDevice(le1Str);
                BluetoothDevice le2Device = mAdapter.getRemoteDevice(le2Str);
                if (le1Device != null) {
                    if (le1Device.getBondState() == BluetoothDevice.BOND_NONE) {
                        Log.d(TAG, "create bond to LE2");
                        if (le2Device != null) {
                            le2Device.createBond(BluetoothDevice.TRANSPORT_LE);
                        }
                    } else if (le1Device.getBondState() == BluetoothDevice.BOND_BONDING) {
                        Log.d(TAG, "cancel bond to LE1");
                        le1Device.cancelBondProcess();
                        SystemClock.sleep(50);
                        if (le2Device != null) {
                            Log.d(TAG, "create bond to LE2");
                            le2Device.createBond(BluetoothDevice.TRANSPORT_LE);
                        }
                    }
                }
            }
        }
    };

    private boolean isLeDeviceBusy() {
        if (mCachedDevice.getLeAudioStatus() == BluetoothDevice.LE_ADUIO_STATE_ON) {
            String leStr = mCachedDevice.findLeAddress();
            String le1Str = "";
            String le2Str = "";
            if (leStr != null) {
                String[] deviceInfo = leStr.split(";");
                if (deviceInfo.length > 1) {
                    le1Str = deviceInfo[0];
                    le2Str = deviceInfo[1];
                }
            }
            if (mAdapter != null && mDeviceManager != null && !("".equals(le1Str)) &&
                    !("".equals(le2Str))) {
                boolean isLe1Busy = false;
                boolean isLe2Busy = false;
                BluetoothDevice le1Device = mAdapter.getRemoteDevice(le1Str);
                BluetoothDevice le2Device = mAdapter.getRemoteDevice(le2Str);
                CachedBluetoothDevice cachedLe1Device = mDeviceManager.findDevice(le1Device);
                if (cachedLe1Device != null) {
                    isLe1Busy = cachedLe1Device.isBusy();
                }
                CachedBluetoothDevice cachedLe2Device = mDeviceManager.findDevice(le2Device);
                if (cachedLe2Device != null) {
                    isLe2Busy = cachedLe2Device.isBusy();
                }
                Log.d(TAG,"isBusy,isLe1Busy = " + isLe1Busy + ",isLe2Busy = " + isLe2Busy);
                if (isLe1Busy || isLe2Busy) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isLeDeviceConnected() {
        if (mCachedDevice.getLeAudioStatus() == BluetoothDevice.LE_ADUIO_STATE_ON) {
            String leStr = mCachedDevice.findLeAddress();
            String le1Str = "";
            String le2Str = "";
            if (leStr != null) {
                String[] deviceInfo = leStr.split(";");
                if (deviceInfo.length > 1) {
                    le1Str = deviceInfo[0];
                    le2Str = deviceInfo[1];
                }
            }
            Log.d(TAG,"isLeDeviceConnected,le1Str = " + le1Str + ",le2Str = " + le2Str);
            if (mAdapter != null && mDeviceManager != null && !("".equals(le1Str)) &&
                    !("".equals(le2Str))) {
                boolean isLe1Connected = false;
                boolean isLe2Connected = false;
                BluetoothDevice le1Device = mAdapter.getRemoteDevice(le1Str);
                BluetoothDevice le2Device = mAdapter.getRemoteDevice(le2Str);
                CachedBluetoothDevice cachedLe1Device = mDeviceManager.findDevice(le1Device);
                if (cachedLe1Device != null) {
                    isLe1Connected = cachedLe1Device.isConnected();
                }
                CachedBluetoothDevice cachedLe2Device = mDeviceManager.findDevice(le2Device);
                if (cachedLe2Device != null) {
                    isLe2Connected = cachedLe2Device.isConnected();
                }
                Log.d(TAG,"isLeDeviceConnected ,isLe1Connected = " + isLe1Connected +
                        ",isLe2Connected = " + isLe2Connected);
                if (isLe1Connected || isLe2Connected){
                    return true;
                }
            }
        }
        return false;
    }
}